package com.cheng.picture.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cheng.picture.exception.BusinessException;
import com.cheng.picture.exception.ErrorCode;
import com.cheng.picture.exception.ThrowUtils;
import com.cheng.picture.manager.upload.FilePictureUpload;
import com.cheng.picture.manager.upload.PictureUploadTemplate;
import com.cheng.picture.manager.upload.UrlPictureUpload;
import com.cheng.picture.model.dto.picture.PictureReviewRequest;
import com.cheng.picture.model.dto.picture.PictureUploadByBatchRequest;
import com.cheng.picture.model.enums.PictureReviewStatusEnum;
import com.cheng.picture.model.vo.PictureVO;
import com.cheng.picture.model.vo.UserVO;
import com.cheng.picture.manager.FileManager;
import com.cheng.picture.mapper.PictureMapper;
import com.cheng.picture.model.dto.file.UploadPictureResult;
import com.cheng.picture.model.dto.picture.PictureQueryRequest;
import com.cheng.picture.model.dto.picture.PictureUploadRequest;
import com.cheng.picture.model.entity.Picture;
import com.cheng.picture.model.entity.User;
import com.cheng.picture.service.PictureService;
import com.cheng.picture.service.UserService;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import lombok.extern.slf4j.XSlf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service
@Slf4j
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
        implements PictureService {

//    @Resource
//    private FileManager fileManager;

    @Resource
    private UserService userService;

    @Resource
    private FilePictureUpload filePictureUpload;

    @Resource
    private UrlPictureUpload urlPictureUpload;


    //本地缓存
    private final Cache<String, String> LOCAL_CACHE =
            Caffeine.newBuilder().initialCapacity(1024)
                    .maximumSize(10000L)
                    // 缓存 5 分钟移除
                    .expireAfterWrite(5L, TimeUnit.MINUTES)
                    .build();

    /**
     * 图片服务参数校验
     * @param picture
     */
    @Override
    public void validPicture(Picture picture) {
        /**
         * 整体参数校验
         */
        ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);

        /**
         * 属性参数校验
         */
        Long id = picture.getId();
        String url = picture.getUrl();
        String introduction = picture.getIntroduction();

        /**
         * 修改数据，id不能为null
         */
        ThrowUtils.throwIf(ObjUtil.isNull(id), ErrorCode.PARAMS_ERROR, "id 不能为空");
        //传递了参数才校验
        if (StrUtil.isNotBlank(url)) {
            ThrowUtils.throwIf(url.length() > 1024, ErrorCode.PARAMS_ERROR, "url 过长");
        }
        if (StrUtil.isNotBlank(introduction)) {
            ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");
        }
    }

    @Override
    public PictureVO uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest, User loginUser) {
        /**
         * 1.判断是否登录
         */
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);


        /**
         * 2.判断是第一次上传，还是重新修改上传(更新)
         */
        Long pictureId = null;
        if (pictureUploadRequest != null)
        {
            pictureId = pictureUploadRequest.getId();
        }


        /**
         *3.更新操作，判断图片是否存在
         */

        //注意：这里要大于0，防止等于0的情况
        if (pictureId != null && pictureId > 0) {
            Picture oldPicture = this.getById(pictureId);
            ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");

            /**
             * 仅本人或管理员可编辑 (注意：添加编辑权限校验)
             */
            if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }


        /**
         *  目录前缀： public/userId（根据用户id划分目录 ）
         */
        String uploadPathPrefix = String.format("public/%s", loginUser.getId());

        //根据inputSource 区分是url还是图片上传

        //默认是图片上传
        PictureUploadTemplate pictureUploadTemplate = filePictureUpload;

        //判断 inputSource 是否是 String 类型的实例。
        if(inputSource instanceof String)
        {
            pictureUploadTemplate = urlPictureUpload;
        }
        //UploadPictureResult uploadPictureResult = fileManager.uploadPicture(multipartFile, uploadPathPrefix);

        UploadPictureResult uploadPictureResult = pictureUploadTemplate.uploadPicture(inputSource, uploadPathPrefix);


        /**
         * 构造要入库的图片信息
         */
        Picture picture = new Picture();
        picture.setUrl(uploadPictureResult.getUrl());
        String picName = uploadPictureResult.getPicName();
        if (pictureUploadRequest != null && StrUtil.isNotBlank(pictureUploadRequest.getPicName())) {
            picName = pictureUploadRequest.getPicName();
        }
        picture.setName(picName);
        picture.setPicSize(uploadPictureResult.getPicSize());
        picture.setPicWidth(uploadPictureResult.getPicWidth());
        picture.setPicHeight(uploadPictureResult.getPicHeight());
        picture.setPicScale(uploadPictureResult.getPicScale());
        picture.setPicFormat(uploadPictureResult.getPicFormat());
        picture.setUserId(loginUser.getId());


        /**
          * 添加图片审核校验
          */
        this.fillReviewParams(picture, loginUser);

        /**
         * 更新：对象属性更新
         */
        if (pictureId != null) {
            // 如果是更新，需要补充 id 和编辑时间
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        }
        /**
         * 插入||更新
         */
        boolean result = this.saveOrUpdate(picture);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败，数据库操作失败");
        return PictureVO.objToVo(picture);
    }

    @Override
    public PictureVO getPictureVO(Picture picture, HttpServletRequest request)
    {
        /**
         *  对象转封装类
         */
        PictureVO pictureVO = PictureVO.objToVo(picture);

        /**
         * 关联用户信息
         */
        Long userId = picture.getUserId();
        if (userId != null && userId > 0) {
            User user = userService.getById(userId);
            // UserVO (用户脱敏封装类)
            UserVO userVO = userService.getUserVO(user);
            pictureVO.setUser(userVO);
        }
        return pictureVO;
    }

    /**
     * 分页获取图片封装
     */
    @Override
    public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage, HttpServletRequest request)
    {
        List<Picture> pictureList = picturePage.getRecords();
        Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());
        if (CollUtil.isEmpty(pictureList)) {
            return pictureVOPage;
        }
        /**
         * 实体的对象转换成封装对象
         */
        // 对象列表 => 封装对象列表
        List<PictureVO> pictureVOList = pictureList.stream()
                .map(PictureVO::objToVo)
                .collect(Collectors.toList());
        /**
         *1. 关联查询用户信息
         */
        // 1,2,3,4
        //1.1获取用户id，set集合.不重复
        Set<Long> userIdSet = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());

        //1.2获取userList
        List<User> users = userService.listByIds(userIdSet);
        // 1 => user1, 2 => user2
        //1.3 :根据userId分组
        Map<Long, List<User>> userIdUserListMap = users.stream()
                .collect(Collectors.groupingBy(User::getId));


        /**
         * 设置用户信息
         * 遍历所有，包含这个userId,赋值信息
         */
        pictureVOList.forEach(pictureVO -> {
            Long userId = pictureVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            pictureVO.setUser(userService.getUserVO(user));
        });
        pictureVOPage.setRecords(pictureVOList);
        return pictureVOPage;
    }

    @Override
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest pictureQueryRequest)
    {
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();

        /**
         * 参数检验
         */
        if (pictureQueryRequest == null) {
            return queryWrapper;
        }

        // 从对象中取值
        Long id = pictureQueryRequest.getId();
        String name = pictureQueryRequest.getName();
        String introduction = pictureQueryRequest.getIntroduction();
        String category = pictureQueryRequest.getCategory();
        List<String> tags = pictureQueryRequest.getTags();
        Long picSize = pictureQueryRequest.getPicSize();
        Integer picWidth = pictureQueryRequest.getPicWidth();
        Integer picHeight = pictureQueryRequest.getPicHeight();
        Double picScale = pictureQueryRequest.getPicScale();
        String picFormat = pictureQueryRequest.getPicFormat();
        String searchText = pictureQueryRequest.getSearchText();
        Long userId = pictureQueryRequest.getUserId();
        String sortField = pictureQueryRequest.getSortField();
        String sortOrder = pictureQueryRequest.getSortOrder();


        //审核字段
        Integer reviewStatus = pictureQueryRequest.getReviewStatus();
        String reviewMessage = pictureQueryRequest.getReviewMessage();
        Long reviewerId = pictureQueryRequest.getReviewerId();



        /**
         * searchText 多字段搜索
         */
        if (StrUtil.isNotBlank(searchText)) {
            // 需要拼接查询条件
            // and (name like "%xxx%" or introduction like "%xxx%")
            queryWrapper.and(
                    qw -> qw.like("name", searchText)
                    .or()
                    .like("introduction", searchText)
            );


        }

        /**
         * 数值类的参数 注意：非0校验
         */
        queryWrapper.eq(ObjUtil.isNotEmpty(id) && id !=0, "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId) && userId !=0, "userId", userId);
        queryWrapper.like(StrUtil.isNotBlank(name), "name", name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        queryWrapper.like(StrUtil.isNotBlank(picFormat), "picFormat", picFormat);
        queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);
        queryWrapper.eq(ObjUtil.isNotEmpty(picWidth) && picWidth !=0, "picWidth", picWidth);
        queryWrapper.eq(ObjUtil.isNotEmpty(picHeight)&& picHeight !=0, "picHeight", picHeight);
        queryWrapper.eq(ObjUtil.isNotEmpty(picSize) && picSize !=0, "picSize", picSize);
        queryWrapper.eq(ObjUtil.isNotEmpty(picScale) && picScale !=0, "picScale", picScale);

        /**
         * json数组查询
         */
        // JSON 数组查询
        if (CollUtil.isNotEmpty(tags)) {
            /* and (tag like "%\"Java\"%" and like "%\"Python\"%") */
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        /**
         * sortField (需要排序的字段)
         */
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);

        /**
         * 审核字段校验
         */
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewStatus) && reviewStatus != 0, "reviewStatus", reviewStatus);
        queryWrapper.like(StrUtil.isNotBlank(reviewMessage), "reviewMessage", reviewMessage);
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewerId) && reviewerId != 0, "reviewerId", reviewerId);
        return queryWrapper;
    }

    /**
     *
     * @param pictureReviewRequest
     * @param loginUser
     */

    @Override
    public void doPictureReview(PictureReviewRequest pictureReviewRequest, User loginUser)
    {
        /**
         * 参数校验 (开始审核，前端传来的状态不能是待审核)
         */
        Long id = pictureReviewRequest.getId();
        Integer reviewStatus = pictureReviewRequest.getReviewStatus();
        PictureReviewStatusEnum enumByValue = PictureReviewStatusEnum.getEnumByValue(reviewStatus);
        if(enumByValue == null || id == null || PictureReviewStatusEnum.REVIEWING.equals(enumByValue))
        {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        /**
         * 业务校验（图片是否存在,是否是已审核状态）
         */
        Picture picture = getById(id);
        ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR);

        ThrowUtils.throwIf(!PictureReviewStatusEnum.PASS.equals(enumByValue)
                , ErrorCode.PARAMS_ERROR, "已审核");

        /**
         * 更新图片审核状态
         */
        picture.setReviewStatus(enumByValue.getValue());
        picture.setReviewTime(new Date());
        picture.setReviewMessage(pictureReviewRequest.getReviewMessage());
        picture.setReviewerId(loginUser.getId());

        boolean result  = this.updateById(picture);

        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "更新图片审核状态失败");
    }

    /**
     * 审核图片通用方法
     * @param picture
     * @param loginUser
     */
    @Override
    public void fillReviewParams(Picture picture, User loginUser) {
        if (userService.isAdmin(loginUser)) {
            /**
             *管理员自动过审
             */
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewerId(loginUser.getId());
            picture.setReviewMessage("管理员自动过审");
            picture.setReviewTime(new Date());
        } else {
            /**
             *非管理员，创建或编辑都要改为待审核
             */
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }
    }

    @Override
    public int uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest, User loginUser) {
        // 校验参数
        String searchText = pictureUploadByBatchRequest.getSearchText();
        Integer count = pictureUploadByBatchRequest.getCount();
        ThrowUtils.throwIf(count > 30, ErrorCode.PARAMS_ERROR, "最多 30 条");

        // 名称前缀默认等于搜索关键词
        String namePrefix = pictureUploadByBatchRequest.getNamePrefix();
        if (StrUtil.isBlank(namePrefix)) {
            namePrefix = searchText;
        }
        // 抓取内容
        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1", searchText);
        Document document;
        try {
            document = Jsoup.connect(fetchUrl).get();
        } catch (IOException e) {
            log.error("获取页面失败", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取页面失败");
        }
        // 解析内容
        Element div = document.getElementsByClass("dgControl").first();
        if (ObjUtil.isEmpty(div)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取元素失败");
        }
        Elements imgElementList = div.select("img.mimg");
        // 遍历元素，依次处理上传图片
        int uploadCount = 0;
        for (Element imgElement : imgElementList) {
            String fileUrl = imgElement.attr("src");
            if (StrUtil.isBlank(fileUrl)) {
                log.info("当前链接为空，已跳过：{}", fileUrl);
                continue;
            }
            // 处理图片的地址，防止转义或者和对象存储冲突的问题
            // codefather.cn?yupi=dog，应该只保留 codefather.cn
            int questionMarkIndex = fileUrl.indexOf("?");
            if (questionMarkIndex > -1) {
                fileUrl = fileUrl.substring(0, questionMarkIndex);
            }
            // 上传图片
            PictureUploadRequest pictureUploadRequest = new PictureUploadRequest();
            pictureUploadRequest.setFileUrl(fileUrl);
            pictureUploadRequest.setPicName(namePrefix + (uploadCount + 1));

            try {
                PictureVO pictureVO = this.uploadPicture(fileUrl, pictureUploadRequest, loginUser);
                log.info("图片上传成功，id = {}", pictureVO.getId());
                uploadCount++;
            } catch (Exception e) {
                log.error("图片上传失败", e);
                continue;
            }
            if (uploadCount >= count) {
                break;
            }
        }
        return uploadCount;
    }


}




